package soundManager;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.SourceDataLine;


public class SoundManager implements Runnable{
	private static SoundManager soundManager = null;
	private static final int	EXTERNAL_BUFFER_SIZE = 128000;
	private Map<String, Sound> soundHashMap = new HashMap<String, Sound>();
	private Map<AudioFormat, SourceDataLine> SourceDataLineHashMap = new HashMap<AudioFormat, SourceDataLine>();
	private String key;
	private ExecutorService execSvc;
	private static Mixer.Info[] info = AudioSystem.getMixerInfo();
	private  static Mixer mixer = AudioSystem.getMixer(info[0]);
	
	private SoundManager(){
		execSvc = Executors.newFixedThreadPool(1);		
	}
	
	public String getKey(){
		return key;
	}
	public void setKey(String key){
		this.key = key;
	}
	public synchronized static SoundManager getInstance() {
		if(soundManager == null) {
			soundManager = new SoundManager();
		}
		return soundManager;
	}
	
	public void setSound(String path){
		if(soundHashMap.containsKey(path)){
			//do nothing
		}
		else{
			Sound s = new Sound(path);
			s.setAudioInputStream(getAudioStreamFromFile(this.getClass().getResource(path).getPath()));
			s.setAudioFormat(getAudioFormat(s.getAudioInputStream()));
			int	nBytesRead = 0;
			int	oldnBytesRead = nBytesRead;
			byte[]	abData = new byte[EXTERNAL_BUFFER_SIZE];			
			while(nBytesRead != -1){
				try
				{
					oldnBytesRead = nBytesRead;
					nBytesRead = s.getAudioInputStream().read(abData, 0, abData.length);
					
				}
				catch (IOException e)
				{
					e.printStackTrace();
				}
			}
			s.setBytesRead(oldnBytesRead);
			s.setSoundData(abData);
			if(!SourceDataLineHashMap.containsKey(s.getAudioFormat())){
				SourceDataLine line = getAudioLine(s.getAudioFormat());
				line.start();
				SourceDataLineHashMap.put(s.getAudioFormat(), line);				
			}
			soundHashMap.put(path, s);
		}
	}
	
	
	public void playAudio(String key){
		setKey(key);
		execSvc.execute(this);
		
	}	
	
	private AudioInputStream getAudioStreamFromFile(String strFilename){	
		File soundFile = new File(strFilename);
		AudioInputStream	audioInputStream = null;
		try{
			audioInputStream = AudioSystem.getAudioInputStream(soundFile);
		}
		catch (Exception e){
			e.printStackTrace();
			System.exit(1);	
		}
		return audioInputStream;
	}
	
	private AudioFormat getAudioFormat(AudioInputStream audioInputStream){
		return audioInputStream.getFormat();
	}
	
	private SourceDataLine getAudioLine(AudioFormat audioFormat){
		SourceDataLine	line = null;
		DataLine.Info	info = new DataLine.Info(SourceDataLine.class, audioFormat);
		try
		{
			line = (SourceDataLine) AudioSystem.getLine(info);
			line.open(audioFormat);
		}
		catch (LineUnavailableException e)
		{
			e.printStackTrace();
			System.exit(1);
		}
		catch (Exception e)
		{
			e.printStackTrace();
			System.exit(1);
		}
		return line;
	}

	private void playAudio( Sound s){
		SourceDataLineHashMap.get(s.getAudioFormat()).flush();
		SourceDataLineHashMap.get(s.getAudioFormat()).write(s.getSoundData(), 0, s.getBytesRead());		
	}
	
	public void run(){
		
			soundHashMap.get(this.key);
			playAudio(soundHashMap.get(this.key));
			Thread.yield();
			
		
	}


}


